﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="With_WeakDelegate.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the NonPublicDelegateType type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Assets;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Collections;
    using Testing;
#if SILVERLIGHT
    using Microsoft.Silverlight.Testing;
#endif

    internal delegate void NonPublicDelegateType();

    [TestClass, Tag("WeakDelegate")]
    public class With_WeakDelegate
    {
        private static class NestedPublicDelegateHost
        {
            public delegate void NestedPublicDelegateType();
        }

        private delegate void NestedPrivateDelegateType();

        public class SystemUnderTestContainer<TDelegate> where TDelegate : class
        {
            #region Fields

            private readonly Func<TDelegate, TDelegate, TDelegate> _addDelegateMethod;
            private readonly Func<Subscriber, TDelegate> _getDelegateMethod;

            #endregion

            public const int SubscriberCount = 10;

            public SystemUnderTestContainer(Func<Subscriber, TDelegate> getDelegateMethod, Func<IDelegatable, TDelegate> delegateFactoryMethod = null,
                Func<TDelegate, TDelegate, TDelegate> addDelegateMethod = null)
            {
                WeakDelegateRuntime.EnsureInitialized(delegateFactoryMethod);

                // initialize handler factory, needed to attach to correct subscriber method
                _getDelegateMethod = getDelegateMethod;
                _addDelegateMethod = addDelegateMethod;

                // create event subscribers
                Subscribers = new CollectableCollection<Subscriber>(SubscriberCount, () => new Subscriber());
            }

            public CollectableCollection<Subscriber> Subscribers { get; set; }

            internal WeakDelegate<TDelegate> SystemUnderTest { get; private set; }

            public IEnumerable<Subscriber> SelectSubscribers(params int[] indices)
            {
                return indices.Select(x => Subscribers[x]);
            }

            public SystemUnderTestContainer<TDelegate> WhenSubscribersHaveBeenAddedAsMultiCast(params int[] indices)
            {
                TDelegate result = null;
                result = indices.Aggregate(result, (r, x) =>
                    _addDelegateMethod(r, _getDelegateMethod(Subscribers[x])));
                SystemUnderTest += result;
                return this;
            }

            public SystemUnderTestContainer<TDelegate> WhenSubscribersReturn(Func<SystemUnderTestContainer<TDelegate>, IEnumerable<Subscriber>> selectSubscribers, object result)
            {
                selectSubscribers(this).ForEach(x => x.SetReturn(result));
                return this;
            }

            public SystemUnderTestContainer<TDelegate> WhenSubscribersHaveBeenAdded(params int[] indices)
            {
                indices.ForEach(x => SystemUnderTest += _getDelegateMethod(Subscribers[x]));
                return this;
            }

            public SystemUnderTestContainer<TDelegate> WhenSubscribersHaveBeenRemove(params int[] indices)
            {
                indices.ForEach(x => SystemUnderTest -= _getDelegateMethod(Subscribers[x]));
                return this;
            }

            public SystemUnderTestContainer<TDelegate> WhenSubscribersAreCollected(params int[] indices)
            {
                Subscribers.Collect(indices);
                return this;
            }

            public SystemUnderTestContainer<TDelegate> WhenInvoked(Action<TDelegate> invoke)
            {
                invoke(SystemUnderTest.Invoke);
                return this;
            }

            public void AssertFlushedInvocationListCount(int expectedCount)
            {
                Assert.AreEqual(expectedCount, SystemUnderTest.GetInvocationList(true).Count);
            }

            public void AssertInvocationListCount(int expectedCount)
            {
                Assert.AreEqual(expectedCount, SystemUnderTest.GetInvocationList(false).Count);
            }

            public SystemUnderTestContainer<TDelegate> WhenUsingFallback(TDelegate fallback)
            {
                SystemUnderTest.UseFallback(fallback);
                return this;
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_added_delegate_is_static : With_WeakAction
        {
            protected override void BecauseOf()
            {
                //Container.SystemUnderTest += new Action
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_adding_and_subscribers_have_been_collected : With_WeakDelegate
        {
            public SystemUnderTestContainer<Action> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0, 1, 2, 3)
                    .WhenSubscribersAreCollected(0, 2)
                    .WhenSubscribersHaveBeenAdded(4);
            }

            [TestMethod]
            public void Should_compact_subscribers()
            {
                Container.AssertInvocationListCount(3);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_adding_multi_cast_delegate : With_WeakDelegate
        {
            private SystemUnderTestContainer<Action<int>> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action<int>>(x => x.OnInvokeAction, null, (x, y) =>
                {
                    x += y;
                    return x;
                })
                    .WhenSubscribersHaveBeenAddedAsMultiCast(0, 1, 2, 3)
                    .WhenInvoked(x => x(1));
            }

            [TestMethod]
            public void Should_notify_all_subscribers()
            {
                Assert.IsTrue(Container.Subscribers.Take(4).All(x => x.LogEntries.Count == 1));
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_creating_and_delegate_type_is_an_action_with_two_many_arguments : With_WeakDelegate
        {
            [TestMethod]
            public void Should_throw_not_supported_exception()
            {
                AssertException.Throws<NotSupportedException>(
                    () => WeakDelegateRuntime.EnsureInitialized<Action<int, int, int, int, int>>(), ExceptionMessages.WeakDelegateTypeArgumentNotSupported);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_delegate_type_is_nested_private : With_WeakDelegate
        {

#if SILVERLIGHT

            [TestMethod]
            public void Should_throw_not_supported_exception()
            {
                AssertException.Throws<NotSupportedException>(
                    () => WeakDelegateRuntime.EnsureInitialized<NestedPrivateDelegateType>(), ExceptionMessages.WeakDelegateTypeMissingDelegateFactoryMethod);
            }

#else

            [TestMethod]
            public void Should_not_throw_not_supported_exception()
            {
                WeakDelegateRuntime.EnsureInitialized<NestedPrivateDelegateType>();
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                var container = new SystemUnderTestContainer<NestedPrivateDelegateType>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x());
                container.Subscribers[0].AssertSingleLogEntry();
            }

#endif
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_delegate_type_is_nested_public_in_private_class : With_WeakDelegate
        {

#if SILVERLIGHT

            [TestMethod]
            public void Should_throw_not_supported_exception()
            {
                AssertException.Throws<NotSupportedException>(
                    () => WeakDelegateRuntime.EnsureInitialized<NestedPublicDelegateHost.NestedPublicDelegateType>(),
                    ExceptionMessages.WeakDelegateTypeMissingDelegateFactoryMethod);
            }

#else
            
            [TestMethod]
            public void Should_not_throw_not_supported_exception()
            {
                WeakDelegateRuntime.EnsureInitialized<NestedPublicDelegateHost.NestedPublicDelegateType>();
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                var container = new SystemUnderTestContainer<NestedPublicDelegateHost.NestedPublicDelegateType>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x());
                container.Subscribers[0].AssertSingleLogEntry();
            }

#endif
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_delegate_type_is_non_public : With_WeakDelegate
        {

#if SILVERLIGHT

            [TestMethod]
            public void Should_throw_not_supported_exception()
            {
                AssertException.Throws<NotSupportedException>(
                    () => WeakDelegateRuntime.EnsureInitialized<NonPublicDelegateType>(), ExceptionMessages.WeakDelegateTypeMissingDelegateFactoryMethod);
            }

#else
            [TestMethod]
            public void Should_not_throw_not_supported_exception()
            {
                WeakDelegateRuntime.EnsureInitialized<NonPublicDelegateType>();
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                var container = new SystemUnderTestContainer<NonPublicDelegateType>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x());
                container.Subscribers[0].AssertSingleLogEntry();
            }

#endif
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_creating_and_generic_argument_is_not_a_delegate_type : With_WeakDelegate
        {
            [TestMethod]
            public void Should_throw_not_supported_exception()
            {
                AssertException.Throws<NotSupportedException>(
                    () => WeakDelegateRuntime.EnsureInitialized<object>(), ExceptionMessages.WeakDelegateTypeArgumentNotSupported);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_creating_with_custom_delegate_factory_due_to_delegate_type_being_private : With_WeakDelegate
        {
            private SystemUnderTestContainer<PrivateDelegate> Container { get; set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<PrivateDelegate>(x => x.OnInvokeAction, x => x.InvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x());
            }

            [TestMethod]
            public void Should_notify_subscribers()
            {
                Container.Subscribers[0].AssertSingleLogEntry();
            }

            #region Nested type: PrivateDelegate

            private delegate void PrivateDelegate();

            #endregion
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_invoked_and_all_subscribers_have_been_collected : With_WeakAction
        {
            protected override void BecauseOf()
            {
                Container.WhenSubscribersHaveBeenAdded(0)
                    .WhenSubscribersAreCollected(0)
                    .WhenInvoked(x => x());
            }

            [TestMethod]
            public void Should_compact_subscribers()
            {
                Container.AssertInvocationListCount(0);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_invoked_with_multiple_subscribers : With_WeakDelegate
        {
            protected SystemUnderTestContainer<Action> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
                    .WhenInvoked(x => x());
            }

            [TestMethod]
            public void Should_notify_all_subscribers()
            {
                Assert.IsTrue(Container.Subscribers.All(x => x.LogEntries.Count == 1));
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_removing_all_handlers : With_WeakAction
        {
            protected override void BecauseOf()
            {
                Container
                    .WhenSubscribersHaveBeenAdded(0, 1, 2)
                    .WhenInvoked(x => x())
                    .WhenSubscribersHaveBeenRemove(0, 1, 2);
            }

            [TestMethod]
            public void Should_return_null()
            {
                Assert.IsNull(Container.SystemUnderTest);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_removing_handlers_and_some_handlers_have_been_collected : With_WeakDelegate
        {
            public SystemUnderTestContainer<Action> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0, 1, 2, 3)
                    .WhenSubscribersAreCollected(0, 2)
                    .WhenSubscribersHaveBeenRemove(3);
            }

            [TestMethod]
            public void Should_compact_subscribers()
            {
                Container.AssertInvocationListCount(1);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_removing_handlers_from_null : With_WeakAction
        {
            protected override void BecauseOf()
            {
                Container.WhenSubscribersHaveBeenRemove(0);
            }

            [TestMethod]
            public void Should_return_null()
            {
                Assert.IsNull(Container.SystemUnderTest);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_removing_handlers_that_have_not_been_added : With_WeakAction
        {
            public WeakDelegate<Action> SystemUnderTestSnapshot;

            protected override void BecauseOf()
            {
                // add some items
                Container
                    .WhenSubscribersHaveBeenAdded(0, 1, 2, 3, 4);

                // snapshot the delegate
                SystemUnderTestSnapshot = Container.SystemUnderTest;

                // remove items that have NOT been added
                Container
                    .WhenSubscribersHaveBeenRemove(5, 6, 7, 8, 9);
            }

            [TestMethod]
            public void Should_return_original_weak_delegate()
            {
                Assert.AreSame(SystemUnderTestSnapshot, Container.SystemUnderTest);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_some_handlers_are_removed : With_WeakAction
        {
            protected override void BecauseOf()
            {
                // add some items
                Container
                    .WhenSubscribersHaveBeenAdded(0, 1, 2, 3, 4)
                    .WhenInvoked(x => x())
                    .WhenSubscribersHaveBeenRemove(2, 3)
                    .WhenInvoked(x => x());
            }

            [TestMethod]
            public void Should_remove_handlers()
            {
                Assert.AreEqual(3, Container.SystemUnderTest.GetInvocationList(false).Count);
            }

            [TestMethod]
            public void Should_stop_notifying_removed_handlers()
            {
                Container.Subscribers
                    .Skip(2)
                    .Take(2)
                    .ForEach(x => x.AssertLogEntryCount(1));
            }

            [TestMethod]
            public void Should_continue_to_notify_remaining_handlers()
            {
                Container.Subscribers
                    .Take(2)
                    .Skip(2)
                    .Take(1)
                    .ForEach(x => x.AssertLogEntryCount(2));
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_action_with_0_arguments : With_WeakDelegate
        {
            protected SystemUnderTestContainer<Action> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x());
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry();
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_action_with_1_arguments : With_WeakDelegate
        {
            protected SystemUnderTestContainer<Action<int>> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action<int>>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x(1));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(1);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_action_with_2_arguments : With_WeakDelegate
        {
            public SystemUnderTestContainer<Action<int, string>> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action<int, string>>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x(1, "1"));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(1, "1");
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_action_with_3_arguments : With_WeakDelegate
        {
            public SystemUnderTestContainer<Action<int, string, double>> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action<int, string, double>>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x(1, "1", 1.0));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(1, "1", 1.0);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_action_with_4_arguments : With_WeakDelegate
        {
            public SystemUnderTestContainer<Action<int, string, double, bool>> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action<int, string, double, bool>>(x => x.OnInvokeAction)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenInvoked(x => x(1, "1", 1.0, true));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(1, "1", 1.0, true);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_func_with_0_arguments : With_WeakDelegate
        {
            protected int Result;
            protected SystemUnderTestContainer<Func<int>> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Func<int>>(x => x.OnInvokeFunc<int>)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenSubscribersReturn(
                        x => x.SelectSubscribers(0), 1)
                    .WhenInvoked(
                        x => Result = x());
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry();
            }

            [TestMethod]
            public void Should_return_correct_result()
            {
                Assert.AreEqual(1, Result);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_func_with_1_arguments : With_WeakDelegate
        {
            protected string Result;
            protected SystemUnderTestContainer<Func<double, string>> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Func<double, string>>(x => x.OnInvokeFunc<double, string>)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenSubscribersReturn(
                        x => x.SelectSubscribers(0), "Result")
                    .WhenInvoked(
                        x => Result = x(1.0));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(1.0);
            }

            [TestMethod]
            public void Should_return_correct_result()
            {
                Assert.AreEqual("Result", Result);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_func_with_2_arguments : With_WeakDelegate
        {
            protected string Result;
            protected SystemUnderTestContainer<Func<long, double, string>> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Func<long, double, string>>(
                        x => x.OnInvokeFunc<long, double, string>)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenSubscribersReturn(
                        x => x.SelectSubscribers(0), "Result")
                    .WhenInvoked(
                        x => Result = x(2L, 1.0));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(2L, 1.0);
            }

            [TestMethod]
            public void Should_return_correct_result()
            {
                Assert.AreEqual("Result", Result);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_func_with_3_arguments : With_WeakDelegate
        {
            protected string Result;
            protected SystemUnderTestContainer<Func<DateTime, long, double, string>> Container { get; private set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Func<DateTime, long, double, string>>(x => x.OnInvokeFunc<DateTime, long, double, string>)
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenSubscribersReturn(
                        x => x.SelectSubscribers(0), "Result")
                    .WhenInvoked(
                        x => Result = x(new DateTime(2000, 1, 1), 2L, 1.0));
            }

            [TestMethod]
            public void Should_invoke_correctly()
            {
                Container.Subscribers[0].AssertSingleLogEntry(new DateTime(2000, 1, 1), 2L, 1.0);
            }

            [TestMethod]
            public void Should_return_correct_result()
            {
                Assert.AreEqual("Result", Result);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_all_delegates_in_a_multicast_list_are_collected : With_WeakAction
        {
            protected override void BecauseOf()
            {
                Container.WhenSubscribersHaveBeenAdded(0, 1, 2)
                    .WhenSubscribersAreCollected(0, 1, 2);
                Assert.IsFalse(Container.SystemUnderTest.QueryIsAlive(true));
            }

            [TestMethod]
            public void Should_be_able_to_access_target()
            {
                Assert.IsNull(Container.SystemUnderTest.Target);
            }

            [TestMethod]
            public void Should_be_able_to_access_method()
            {
                Assert.IsNotNull(Container.SystemUnderTest.Method);
            }

            [TestMethod]
            public void Should_return_empty_invocationlist()
            {
                Assert.AreEqual(0, Container.SystemUnderTest.GetInvocationList(false).Count);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_using_fallback_and_subscriber_has_been_collected : With_WeakAction
        {
            protected static int FallbackInvokeCount { get; set; }

            protected override void BecauseOf()
            {
                Container
                    .WhenSubscribersHaveBeenAdded(0)
                    .WhenUsingFallback(() => { FallbackInvokeCount++; })
                    .WhenInvoked(x => x())
                    .WhenSubscribersAreCollected(0)
                    .WhenInvoked(x => x());
            }

            [TestMethod]
            public void Should_apply_fallback_when_target_is_collected()
            {
                Assert.AreEqual(1, FallbackInvokeCount);
            }
        }

        [TestClass, Tag("WeakDelegate")]
        public class When_attempting_to_use_non_static_fallback : With_WeakAction
        {
            protected override void BecauseOf()
            {
                Container.WhenSubscribersHaveBeenAdded(0);
            }

            [TestMethod]
            public void Should_throw_argument_exception()
            {
                AssertException.Throws<ArgumentException>(
                    () => Container.WhenUsingFallback(NonStaticFallback));
            }

            public void NonStaticFallback()
            {
            }
        }
        
        public abstract class With_WeakAction : With_WeakDelegate
        {
            public SystemUnderTestContainer<Action> Container;

            [TestInitialize]
            public void TestInitialize()
            {
                Container = new SystemUnderTestContainer<Action>(x => x.OnInvokeAction);
                BecauseOf();
            }

            protected virtual void BecauseOf()
            {
                
            }
        }

        /// <summary>
        /// Verifies that private methods can be invoked by the WeakDelegate.
        /// </summary>
        /// <remarks>
        /// Generally private methods cannot be invoked through reflection within Silverlight, due 
        /// to this I was fully expecting the WeakDelegate class to not work (directly) with 
        /// private methods. Fortunetly this was not true, as the implementation of WeakDelegate
        /// allready used a documented method for invoking private methods 
        /// http://msdn.microsoft.com/en-us/library/ms228976(VS.95).aspx 
        /// </remarks>
        [TestClass, Tag("WeakDelegate")]
        public class When_target_method_is_non_public 
        {
            [TestMethod]
            public void Should_not_throw_method_access_exception()
            {
                // get the method we want to invoke
                var privateMethod = typeof(PrivateSubscriber).GetMethod("OnInvoked",
                    BindingFlags.NonPublic | BindingFlags.Instance);
                
                // create a subscriber instance 
                var subscriber = new PrivateSubscriber();
                
                // create a weak action to invoke the privateMethod
                var weakAction = new WeakDelegate<Action>(
                    subscriber, privateMethod);
                
                // invoke (if we were using MethodInfo.Invoke here we'd expect to get a MethodAccessException)
                weakAction.Invoke();
                
                // we invoked!! (hopefully)
                Assert.AreEqual(1, subscriber.InvokeCount);
            }
        }
    }
}